﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using MacomberMapSystem.Common.Integration;

namespace MacomberMapSystem.Common.User_Interfaces.Mapping
{
    /// <summary>
    /// This class provides a mini-map 
    /// </summary>
    public partial class ctlMiniMap : UserControl
    {
        #region Variable declarations
        /// <summary>The network map display of the main map</summary>
        public MM_NetworkMap_Display MapDisplay;

        /// <summary>The network map associated with the mini-map</summary>
        public ctlNetworkMap NetworkMap;

        /// <summary>Our collection of state bounds</summary>
        private Point[] StateBoundCoordinates;
        #endregion

        #region Initialization
        /// <summary>
        /// Intialize a new mini-map
        /// </summary>
        public ctlMiniMap()
        {
            InitializeComponent();
            this.Resize += new EventHandler(ctlMiniMap_Resize);
            this.DoubleBuffered = true;
        }

        /// <summary>
        /// Update our base bitmap image based on our size
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ctlMiniMap_Resize(object sender, EventArgs e)
        {
            if (this.NetworkMap != null)
            {
                //Create our new blank state map
                Bitmap NewBitmap = new Bitmap(DisplayRectangle.Width, DisplayRectangle.Height, PixelFormat.Format32bppArgb);
                Graphics g = Graphics.FromImage(NewBitmap);

                //Determine the points that will make up the boundary
                Point LastPoint = Point.Empty;                
                List<Point> OutPoints = new List<Point>();
                foreach (PointF LatLng in NetworkMap.Repository.Db.StateBoundary.Coordinates)
                {
                    Point NewPoint = Point.Round(ConvertPoint(LatLng));
                    if (NewPoint.X != LastPoint.X && NewPoint.Y != LastPoint.Y)
                    {
                        OutPoints.Add(NewPoint);
                        LastPoint = NewPoint;
                    }
                }
                StateBoundCoordinates = OutPoints.ToArray();

            }

        }

        /// <summary>
        /// Convert a Latitude/Longitude to single-precision X/Y coordinate on the mini-map.
        /// If the map is in state view, use the state boundaries. If not, use the lasso boundaries
        /// </summary>
        /// <param name="LatLng">The Lat/Long to convert</param>
        /// <returns>The resulting point on the mini-map</returns>
        private PointF ConvertPoint(PointF LatLng)
        {
            MM_Boundary StateBoundary = NetworkMap.Repository.Db.StateBoundary;
            return new PointF(
                (float)DisplayRectangle.Width * (LatLng.X - StateBoundary.Min.X) / (StateBoundary.Max.X - StateBoundary.Min.X),
                (float)DisplayRectangle.Height * (LatLng.Y - StateBoundary.Max.Y) / (StateBoundary.Min.Y - StateBoundary.Max.Y));
        }

        /// <summary>
        /// Convert a Latitude/Longitude to single-precision X/Y coordinate on the mini-map.
        /// If the map is in state view, use the state boundaries. If not, use the lasso boundaries
        /// </summary>
        /// <param name="LatLng">The Lat/Long to convert</param>
        /// <returns>The resulting point on the mini-map</returns>
        private PointD ConvertPoint(PointD LatLng)
        {
            MM_Boundary StateBoundary = NetworkMap.Repository.Db.StateBoundary;
            return new PointD(
                (double)DisplayRectangle.Width * (LatLng.X - StateBoundary.Min.X) / (StateBoundary.Max.X - StateBoundary.Min.X),
                (double)DisplayRectangle.Height * (LatLng.Y - StateBoundary.Max.Y) / (StateBoundary.Min.Y - StateBoundary.Max.Y));
        }


        /// <summary>
        /// Convert two Latitude/Longitude points to a single-precision X/Y rectangle on the mini-map
        /// </summary>
        /// <param name="Start">The first point to convert</param>
        /// <param name="End">The second point to convert</param>
        /// <returns>The resulting point on the mini-map</returns>
        private Rectangle ConvertRectangle(PointD Start, PointD End)
        {
            Start = ConvertPoint(Start);
            End = ConvertPoint(End);
            return new Rectangle((int)(Start.X > End.X ? End.X : Start.X), (int)(Start.Y > End.Y ? End.Y : Start.Y), (int)Math.Abs(End.X - Start.X), (int)Math.Abs(End.Y - Start.Y));
        }

        /// <summary>
        /// Link the mini-map to the network map
        /// </summary>
        /// <param name="NetworkMap"></param>
        public void LinkMap(ctlNetworkMap NetworkMap)
        {
            this.NetworkMap = NetworkMap;
            BackColor = Color.Transparent;
            MapDisplay = NetworkMap.DisplayOptions;
            MapDisplay.CoordinatesPan += new EventHandler(RefreshMap);
            MapDisplay.CoordinatesZoomed += new EventHandler(RefreshMap);
            MapDisplay.MapTransparencyChanged += new EventHandler(RefreshMap);
            ctlMiniMap_Resize(this, EventArgs.Empty);
        }

        


        /// <summary>
        /// When our coordinates change, refresh our map
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RefreshMap(object sender, EventArgs e)
        {
            this.Refresh();
        }
        #endregion


        #region Rendering            
        /// <summary>
        /// Paint our mini-map
        /// </summary>        
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (NetworkMap == null)
                e.Graphics.DrawString("Control not\ninitialized", this.Font, Brushes.White, PointF.Empty);
            else
            {
                //Draw our state and fill it in
                int GrayLevel = (int)(NetworkMap.DisplayOptions.MapTransparency * 255f);
                e.Graphics.Clear(Color.FromArgb(GrayLevel,GrayLevel,GrayLevel));
                e.Graphics.FillPolygon(Brushes.Black, StateBoundCoordinates);
                e.Graphics.DrawPolygon(Pens.DarkGray, StateBoundCoordinates);
                DrawMapPosition(e.Graphics, MapDisplay.TopLeft, MapDisplay.BottomRight);
            }
        }

        /// <summary>
        /// Draw the white rectangle signifying the map's position
        /// </summary>
        /// <param name="g">Graphics connector</param>
        /// <param name="TopLeft">Top-left point</param>
        /// <param name="BottomRight">Bottom-right point</param>
        private void DrawMapPosition(Graphics g, PointD TopLeft, PointD BottomRight)
        {
            Rectangle OutRect = ConvertRectangle(TopLeft, BottomRight);
            if (TopLeft.IsEmpty && BottomRight.IsEmpty)
                return;
            g.DrawRectangle(Pens.LightGray, OutRect);
            if (OutRect.Width < 6)
            {
                PointF MidPoint = new PointF(OutRect.X + (OutRect.Width / 2f), OutRect.Y + (OutRect.Height / 2f));
                g.DrawLine(Pens.LightGray, 0, MidPoint.Y, OutRect.Left, MidPoint.Y);
                g.DrawLine(Pens.LightGray, OutRect.Right, MidPoint.Y, DisplayRectangle.Width, MidPoint.Y);
                g.DrawLine(Pens.LightGray, MidPoint.X, 0, MidPoint.X, OutRect.Top);
                g.DrawLine(Pens.LightGray, MidPoint.X, OutRect.Bottom, MidPoint.X, DisplayRectangle.Height);
            }
        }
        #endregion

        #region Mouse handling
        /// <summary>
        /// Handle mouse down
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                OnMouseMove(e);
            base.OnMouseDoubleClick(e);
        }

        /// <summary>
        /// Handle mouse movement
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            //If the user is pressing the left mouse button and can zoom/pan, do it
            if (e.Button == MouseButtons.Left)
            {
                MapDisplay.SetCenter(UnconvertPoint(e.Location), NetworkMap);
                this.Invalidate();
                NetworkMap.Refresh();

            }
            base.OnMouseMove(e);
        }

        /// <summary>
        /// Handle mouse wheel
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            //If the user is moving the mouse wheel and they can zoom, do it.
            MapDisplay.ZoomLevel += Math.Sign(e.Delta);
            this.Invalidate();
        }

        /// <summary>
        /// Convert a single-precision X/Y coordinate on the mini-map to a Latitude/Longitude
        /// </summary>
        /// <param name="XY">The point on the mini-map</param>
        /// <returns>The resulting lat/long</returns>        
        private PointD UnconvertPoint(Point XY)
        {
            MM_Boundary StateBoundary = NetworkMap.Repository.Db.StateBoundary;
            return new PointD(
                (((double)XY.X / (double)DisplayRectangle.Width) * (StateBoundary.Max.X - StateBoundary.Min.X)) + StateBoundary.Min.X,
                (((double)XY.Y / (double)DisplayRectangle.Height) * (StateBoundary.Min.Y - StateBoundary.Max.Y)) + StateBoundary.Max.Y);
        }
        #endregion
    }
}